package engine;

import java.util.ArrayList;

import joint.JointManager;
import joint.JointOperator;
import massDistribution.ClassAttributeMap;
import massDistribution.JointMassDistribution;
import massDistribution.MassDistribution;
import core.FrameOfDiscernment;
import exception.DempsterTotalConflictException;
import exception.JointNotPossibleException;
import exception.MassDistributionNotValidException;
import exceptions.NullSourcesException;

/**
 * This class manages all the concepts due to the Reputation System. It allows
 * one to compute the reputation of a {@link ServiceStub} using the
 * Demster-Shafer Theory.
 * 
 * @author Elisa Costante
 */
public class ReputationManager {

	/**
	 * @uml.property name="frameOfDiscernment"
	 * @uml.associationEnd
	 */
	private FrameOfDiscernment frameOfDiscernment;
	/**
	 * @uml.property name="attribute"
	 * @uml.associationEnd
	 */
	private ClassAttributeMap attributeMap;

	public ReputationManager(ClassAttributeMap attributeMap,
			FrameOfDiscernment frameOfDiscernment) {
		this.attributeMap = attributeMap;
		this.frameOfDiscernment = frameOfDiscernment;

	}

	/**
	 * Computes and returns the reputation value for the {@link ServiceStub}
	 * <code>candidateService</code>
	 * 
	 * @param aggregator
	 *            : the kind of aggregator one wants to use in order to
	 *            aggregate information coming from different sources.
	 * 
	 * 
	 * @param candidateService
	 *            : the service one wants to compute the reputation.
	 * @param sources
	 *            : the {@link ExternalSource} which will be query in order to
	 *            obtain the reputation.
	 * @return a {@link MassDistribution} wich rapresents the reputation of the
	 *         candidateService.
	 * @throws NullSourcesException
	 */
	public JointMassDistribution getReputation(JointOperator aggregator,
			Service candidateService, ArrayList<ExternalSource> sources)
			throws NullSourcesException {

		if (candidateService != null && sources != null && aggregator != null
				&& sources.size() > 0) {

			ArrayList<MassDistribution> allMasses = new ArrayList<MassDistribution>();
			for (ExternalSource source : sources) {
				if (source.getMeasuredServices() != null
						&& source.getMeasuredServices().contains(
								candidateService)) {
					// Get the mass distribution of the services
					MassDistribution serviceMass = readMassDistribution(
							candidateService, source);
					allMasses.add(serviceMass);
				} else {
					allMasses.add(MassDistribution
							.getLackOfKnowledge(frameOfDiscernment));
				}
			}
			// Compute reputation
			JointMassDistribution reputation = null;
			try {
				reputation = computeReputation(aggregator, allMasses);
			} catch (MassDistributionNotValidException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (JointNotPossibleException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (DempsterTotalConflictException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return reputation;

		} else {
			throw new NullSourcesException();
		}

	}

	/**
	 * Return the repuation obtained from the joint operation.
	 * 
	 * @param aggregator
	 * @param allMasses
	 * @return
	 * @throws JointNotPossibleException
	 * @throws MassDistributionNotValidException
	 * @throws DempsterTotalConflictException
	 */
	private JointMassDistribution computeReputation(JointOperator aggregator,
			ArrayList<MassDistribution> allMasses)
			throws MassDistributionNotValidException,
			JointNotPossibleException, DempsterTotalConflictException {

		JointMassDistribution jointMass = JointManager.applyOperator(allMasses,
				aggregator, frameOfDiscernment);
		return jointMass;

	}

	/**
	 * @param candidateService
	 * @param source
	 * @return
	 */
	private MassDistribution readMassDistribution(Service candidateService,
			ExternalSource source) {
		Service servicefromSource = source.getMeasuredService(candidateService
				.getIdentifierName());
		MassDistribution serviceMass = servicefromSource.getMassDistribution(
				frameOfDiscernment, attributeMap);
		return serviceMass;
	}

	/**
	 * Returns all the jointmass that is possible to compute with all the
	 * operator defined in JointOperator.
	 * 
	 * @return
	 * @throws JointNotPossibleException
	 * @throws NullSourcesException
	 * @throws MassDistributionNotValidException
	 */
	public ArrayList<JointMassDistribution> getAllJointMasses(
			Service candidateService, ArrayList<ExternalSource> sources)
			throws JointNotPossibleException, NullSourcesException {

		JointOperator[] allOperator = JointOperator.values();
		ArrayList<JointMassDistribution> allJointMass = new ArrayList<JointMassDistribution>();

		for (int i = 0; i < allOperator.length; i++) {
			JointMassDistribution reputation = getReputation(allOperator[i],
					candidateService, sources);
			allJointMass.add(reputation);
		}
		return allJointMass;
	}

}
